#include <fstream>

#include <log.hpp>
#include <memfs.h>
#include "scu.h"
#include "sc.h"

#include <string.h>

static CLog clg;

table_t edges;

typedef struct edge_s
{
  int source, dest;
  float head, tail;
} edge_t;

edge_t *t2dFindEdge(int source, int dest)
{
  edge_t *edge;
  int id=-1;

  while (tblgetnextelement(&edges, &id, &edge))
  {
    if (edge->source == source && edge->dest == dest)
      return edge;
  }

  tbladdnew(&edges, &edge);
  edge->source = source;
  edge->dest = dest;

  return edge;
}

void t2dWriteOperation(Mshell &shell, std::ofstream &outp)
{
  int optype, line, count;
  char *element, *element2, *opname;

  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("optype", &optype));
  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("line", &line));
  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("opname", &opname));
  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("count", &count));

  outp << "o" << line << " [style = filled, color = white, shape=box, label=\"" << opname << " (" << line << ")\"];"
       << std::endl;

  abortOnError(STREAM(clg.err.GetStream()), shell.FindInitDirectoryElements("arguments"));
  while (!shell.FindGetNextElement(&element))
  {
    Mshell ashell(shell, element);
    int atype, intent;

    abortOnError(STREAM(clg.err.GetStream()), ashell.GetContent("type", &atype));
    abortOnError(STREAM(clg.err.GetStream()), ashell.GetContent("intent", &intent));

    if (atype == 0)
    {
      abortOnError(STREAM(clg.err.GetStream()), ashell.FindInitDirectoryElements("peers"));
      while (!ashell.FindGetNextElement(&element2))
      {
	Mshell pshell(ashell, element2);
	int pline, pcount;

	abortOnError(STREAM(clg.err.GetStream()), pshell.GetContent("line", &pline));
	abortOnError(STREAM(clg.err.GetStream()), pshell.GetContent("count", &pcount));

	if (intent)
	{
	  edge_t *edge = t2dFindEdge(line, pline);
	  edge->tail = ((float)pcount/count)*100;
	}
	else
	{
	  edge_t *edge = t2dFindEdge(pline, line);
	  edge->head = ((float)pcount/count)*100;
	}	  
      }
    }
  }
}

void t2dWriteConditional(Mshell &shell, std::ofstream &outp)
{
  int ctype, line, count;
  char *element, *element2, *ctypetext;

  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("ctype", &ctype));
  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("line", &line));
  abortOnError(STREAM(clg.err.GetStream()), shell.GetContent("count", &count));

  switch(ctype)
  {
    case -1: ctypetext = "MAIN"; break;
    case IF: ctypetext = "IF"; break;
    case SWITCH: ctypetext = "SWITCH"; break;
    case LOOP: ctypetext = "LOOP"; break;
    default: ctypetext = "UNKNOWN"; break;
  }

  outp << "subgraph cluster_conditional" << line << " {" << std::endl
       << "  style = filled;" << std::endl
       << "  color = white;" << std::endl
       << "  label = \"" << ctypetext << " (line " << line << ")\";" << std::endl;

  abortOnError(STREAM(clg.err.GetStream()), shell.FindInitDirectoryElements("conditions"));
  while (!shell.FindGetNextElement(&element))
  {
    Mshell cshell(shell, element);
    int value, ccount;

    abortOnError(STREAM(clg.err.GetStream()), cshell.GetContent("value", &value));
    abortOnError(STREAM(clg.err.GetStream()), cshell.GetContent("count", &ccount));

    outp << "subgraph cluster_conditional" << line << "condition" << value << " {" << std::endl
	 << "  style = filled;" << std::endl
	 << "  color = lightgrey;" << std::endl;
    
    if (ccount != count)
      outp << "  label = \"case " << value << " (" << ((float)ccount/count)*100 << "%)\";";
    else
      outp << "  label = \"\";";

    outp << std::endl;

    abortOnError(STREAM(clg.err.GetStream()), cshell.FindInitDirectoryElements("children"));
    while (!cshell.FindGetNextElement(&element2))
    {
      Mshell sshell(cshell, element2);
      int stype;
      
      abortOnError(STREAM(clg.err.GetStream()), sshell.GetContent("type", &stype));

      if (stype == 0)
	t2dWriteConditional(sshell, outp);
      else
	t2dWriteOperation(sshell, outp);
    }
    
    outp << "}" << std::endl;
  }

  outp << "}" << std::endl;
}

void t2dConnectOperations(std::ofstream &outp)
{
  int id=-1;
  edge_t *edge;

  while (tblgetnextelement(&edges, &id, &edge))
  {
    outp << "o" << edge->source << " -> o" << edge->dest << "[";

    if (edge->head == edge->tail && edge->head != 100)
    {
      outp << "label=\"" << edge->head << "%\",";
    }
    else
    {
      if (edge->head != 100)
	outp << "headlabel=\"" << edge->head << "%\",";
      if (edge->tail != 100)
	outp << "taillabel=\"" << edge->tail << "%\",";
    }

    outp << "dir=forward]" << std::endl;
  }  
}

extern "C" int MAIN(int argc, char **argv)
{
  if ((argc != 3 && argc != 4) || (argc == 4 && strcmp(argv[1], "--collapsed") && strcmp(argv[1], "--pure")))
  {
    clg.err() << "Usage: " << argv[0] << "[--collapsed|--pure] <xml trace> <dot trace>" << std::endl;
    return -1;
  }

  if (!strcmp(argv[1], "--collapsed"))
  {
    Mfs tfs;
    Mshell tshell(tfs);
    std::ifstream inp(argv[2]);
    std::ofstream outp(argv[3]);
    XMLParser parser(inp);
    
    abortOnError(STREAM(clg.err.GetStream()), !inp.is_open());
    abortOnError(STREAM(clg.err.GetStream()), !outp.is_open());
    abortOnError(STREAM(clg.err.GetStream()), tshell.Import(parser, "/"));
    inp.close();

    tblcreate(&edges, sizeof(edge_t));

    tshell.ChDir("trace/conditionals/0");

    outp.precision(2);
    outp << "digraph G {" << std::endl;
    outp << "  label = \"" << argv[2] << "\";" << std::endl;
    outp << "  fontsize = 10;" << std::endl;
    outp << "  labeljust = r;" << std::endl;
    outp << "  center = true;" << std::endl;
    outp << "  node [fontsize = 11];" << std::endl;
    outp << "  edge [fontsize = 11, labelfloat=true, labeldistance=2, arrowsize=0.5];" << std::endl;


    t2dWriteConditional(tshell, outp);
    t2dConnectOperations(outp);

    outp << "}" << std::endl;
    outp.close();
  }
  else if (!strcmp(argv[1], "--pure"))
  {
    scuStatRead(argv[2]);
    scuStatWriteStructure(argv[3], 1);
  }
  else
  {
    scuStatRead(argv[1]);
    scuStatWriteStructure(argv[2], 0);
  }

  return 0;
}
